﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using YAPL.Language.Core;
using YAPL.Language.Core.Types;
using YAPL.Util;

namespace YAPL.Language.Compiler.CodeGen.ReflectionEmitGenerator.AstGen.Helpers {
	/// <summary>
	/// Helper for emitting various function parts
	/// </summary>
	public static class ILGeneratorFunctionExtensions {
		/// <summary>
		/// Emits code which creates instance of a function
		/// </summary>
		/// <param name="ilGen">IL Generator provided</param>
		/// <param name="functionClass">type parameter, which represents function class</param>
		public static void emitCreateFunction(this ILGenerator ilGen, Type functionClass) {
			var functionCtor = functionClass.GetConstructor(new[] {typeof(YAPLClass)});
			ilGen.Emit(OpCodes.Newobj, functionCtor);
			ilGen.Emit(OpCodes.Pop);
		}

		/// <summary>
		/// Emits code for function call. Parameters (function instance and scope) should already be on top of evaluation stack
		/// </summary>
		/// <param name="ilGen">IL Generator provided</param>
		public static void EmitFunctionCall(this ILGenerator ilGen, LocalBuilder scopeVar) {
			var callMethod = typeof(YAPLFunction).GetMethod(YAPLFunction.CALL_METHOD_NAME, new[] { typeof(Scope) });
			ilGen.Emit(OpCodes.Ldloc, scopeVar);
			ilGen.Emit(OpCodes.Callvirt, callMethod);
			//ilGen.Emit(OpCodes.Pop);
		}

		/// <summary>
		/// Emits call of 'ParseScope' method (should always be in the beginning of 'Call' function)
		/// </summary>
		/// <param name="ilGen">IL Generator provided</param>
		public static void EmitParseScopeMethod(this ILGenerator ilGen) {
			var attrs = BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public;
			var parseScopeMethod = typeof (YAPLFunction).GetMethod(YAPLFunction.PARSE_SCOPE_METHOD_NAME, attrs);
			ilGen.Emit(OpCodes.Ldarg_0); //'this' pointer
			ilGen.Emit(OpCodes.Ldarg_1); //scope argument
			ilGen.Emit(OpCodes.Call, parseScopeMethod); //emit method call
		}

		/// <summary>
		/// Emits code for function class constructor
		/// </summary>
		/// <param name="ilGen">IL Generator provided</param>
		/// <param name="functionName">name of a function</param>
		/// <param name="owner">function owner class</param>
		public static void EmitFunctionConstructorCode(this ILGenerator ilGen, string functionName) {
			var attrs = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
			var parentCtor = typeof (YAPLFunction).GetConstructor(attrs, 
				null,
				new[] {typeof (String), typeof (YAPLClass)},
				null);
			//load parent constructor arguments on stack
			ilGen.Emit(OpCodes.Ldarg_0); //load 'this' pointer
			ilGen.Emit(OpCodes.Ldstr, functionName);

			ilGen.Emit(OpCodes.Ldarg_1);
			ilGen.Emit(OpCodes.Call, parentCtor);
			ilGen.Emit(OpCodes.Ret);
		}
	}
}
